/****************************************************************************
**
** WX公众号:Qt历险记
** CSDN:Qt魔术师
**
****************************************************************************/
#include "themewidget.h"
#include "ui_themewidget.h"

// 示例需要的头文件
#include <QtCharts/QChartView>
#include <QtCharts/QPieSeries>                  // 饼图的形式显示数据
#include <QtCharts/QPieSlice>                   // 扇形区
#include <QtCharts/QAbstractBarSeries>          // 序列类的抽象父类
#include <QtCharts/QPercentBarSeries>           // 条形序列百分比
#include <QtCharts/QStackedBarSeries>           // 垂直堆叠的条
#include <QtCharts/QBarSeries>                  // 按类别分组的竖条表示一系列数据
#include <QtCharts/QBarSet>                     // 表示条形图中的一组条形图
#include <QtCharts/QLineSeries>                 // 以折线图的形式显示数据
#include <QtCharts/QSplineSeries>               // 以样条图的形式表示数据
#include <QtCharts/QScatterSeries>              // 以散点图表示数据
#include <QtCharts/QAreaSeries>                 // 以面积图表示数据
#include <QtCharts/QLegend>                     // 显示了一个图表的图例
#include <QtWidgets/QGridLayout>                // 在网格中布局小部件
#include <QtWidgets/QFormLayout>                // 管理输入小部件的形式及其相关的标签
#include <QtWidgets/QComboBox>                  // 一个组合的按钮和弹出式列表
#include <QtWidgets/QSpinBox>                   // 提供了一个旋转箱小部件
#include <QtWidgets/QCheckBox>                  // 提供了一个带有文本标签的复选框
#include <QtWidgets/QGroupBox>                  // 提供了一个带有标题的组框框架
#include <QtWidgets/QLabel>                     // 提供了一个文本或图像显示
#include <QtCore/QRandomGenerator>              // 随机数生成器中获得随机值
#include <QtCharts/QBarCategoryAxis>            // 将类别添加到图表的轴
#include <QtWidgets/QApplication>               // 应用程序类管理gui应用程序的控制流和主要设置
#include <QtCharts/QValueAxis>                  // 将值添加到图表的轴上

#include <QDebug>

ThemeWidget::ThemeWidget(QWidget *parent) :
    QWidget(parent),
    m_listCount(3),     // 初始化为3个
    m_valueMax(10),     // 最大值10
    m_valueCount(7),    // 值的数量7个
    m_dataTable(generateRandomData(m_listCount, m_valueMax, m_valueCount)), // 3组数据 每组7个
    m_ui(new Ui_ThemeWidgetForm)    // ui界面
{
    m_ui->setupUi(this);
    populateThemeBox();         // 主题初始化
    populateAnimationBox();     // 动画初始化
    populateLegendBox();

    // 创建图表

    QChartView *chartView;

    chartView = new QChartView(createAreaChart());
    m_ui->gridLayout->addWidget(chartView, 1, 0);
    m_charts << chartView;

    chartView = new QChartView(createPieChart());
    // 如果饼片标签不适合屏幕，就会发生有趣的事情，所以我们忽略了大小策略
    chartView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    m_ui->gridLayout->addWidget(chartView, 1, 1);
    m_charts << chartView;

    //![5]
    chartView = new QChartView(createLineChart());
    m_ui->gridLayout->addWidget(chartView, 1, 2);
    //![5]
    m_charts << chartView;

    chartView = new QChartView(createBarChart(m_valueCount));
    m_ui->gridLayout->addWidget(chartView, 2, 0);
    m_charts << chartView;

    chartView = new QChartView(createSplineChart());
    m_ui->gridLayout->addWidget(chartView, 2, 1);
    m_charts << chartView;

    chartView = new QChartView(createScatterChart());
    m_ui->gridLayout->addWidget(chartView, 2, 2);
    m_charts << chartView;

    // Set defaults
    m_ui->antialiasCheckBox->setChecked(true);

    // 将灯光主题中的颜色设置为默认颜色
    QPalette pal = qApp->palette();
    pal.setColor(QPalette::Window, QRgb(0xf0f0f0));
    pal.setColor(QPalette::WindowText, QRgb(0x404044));
    qApp->setPalette(pal);

    updateUI();
}

ThemeWidget::~ThemeWidget()
{
    delete m_ui;
}

// 默认 第一次listCount = 3  valueMax = 10   valueCount = 7 【图表值⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐】
DataTable ThemeWidget::generateRandomData(int listCount, int valueMax, int valueCount) const
{
    DataTable dataTable;    // 对象链表集合

    qDebug()<<"随机数最大范围值："<<valueMax / (qreal) valueCount;   // 默认保留了5位小数（随机数最大范围值： 1.42857）
    // 生成随机数据 3行7列=21个数据
    for (int i(0); i < listCount; i++) {
        DataList dataList; // 数据集合对象链表
        qreal yValue(0);
        for (int j(0); j < valueCount; j++) { // 0-7 7个为一组 每一组yValue都是递增的
            //生成一个随机数，随机数范围不超过 valueMax / (qreal) valueCount
            yValue = yValue + QRandomGenerator::global()->bounded(valueMax / (qreal) valueCount);   // 这里的(qreal)是用于进行类型转换，确保进行浮点数除法计算。
            //qDebug()<<"j = "<<j<<" yValue = "<<yValue<<" valueMax = "<<valueMax<<" valueCount = "<<valueCount;

            // 生成一个0到1之间的随机浮点数(六位小数)QRandomGenerator::global()->generateDouble())
            // ((qreal) m_valueMax / (qreal) valueCount)：计算了每个区间的宽度x之间的间隔宽度
            // 结果：(j * (m_valueMax / valueCount), (j+1) * (m_valueMax / valueCount))范围内的随机浮点数。
            QPointF value((j + QRandomGenerator::global()->generateDouble()) * ((qreal) m_valueMax / (qreal) valueCount),
                          yValue);
            // qDebug()<<"j = "<<j<<" value = "<<value;    // x,y坐标
            QString label = "Slice " + QString::number(i) + ":" + QString::number(j);
            dataList << Data(value, label); // 共3组 每组7个
        }
        dataTable << dataList;  // 共3组
        qDebug()<<"i = "<<i<<" dataTable = "<<dataTable<<Qt::endl;
    }

    return dataTable;
}

// 填充主题框 图标的一些颜色
void ThemeWidget::populateThemeBox()
{
    // 向主题组合框中添加项目
    m_ui->themeComboBox->addItem("Light", QChart::ChartThemeLight);                 // 光的主题，这是默认的主题。
    m_ui->themeComboBox->addItem("Blue Cerulean", QChart::ChartThemeBlueCerulean);  // 天蓝色的主题。
    m_ui->themeComboBox->addItem("Dark", QChart::ChartThemeDark);                   // 黑暗的主题。
    m_ui->themeComboBox->addItem("Brown Sand", QChart::ChartThemeBrownSand);        // 沙棕色的主题。
    m_ui->themeComboBox->addItem("Blue NCS", QChart::ChartThemeBlueNcs);            // 自然色彩系统（ncs）的蓝色主题。
    m_ui->themeComboBox->addItem("High Contrast", QChart::ChartThemeHighContrast);  // 图表主题高对比度
    m_ui->themeComboBox->addItem("Blue Icy", QChart::ChartThemeBlueIcy);            // 冰冷的蓝色主题。
    m_ui->themeComboBox->addItem("Qt", QChart::ChartThemeQt);                       // qt主题。
}

// 填充动画框
void ThemeWidget::populateAnimationBox()
{
    // 将项目添加到动画组合框中
    m_ui->animatedComboBox->addItem("No Animations", QChart::NoAnimation);              // 动画在图表中被禁用。这是默认值。
    m_ui->animatedComboBox->addItem("GridAxis Animations", QChart::GridAxisAnimations); // 网格轴动画
    m_ui->animatedComboBox->addItem("Series Animations", QChart::SeriesAnimations);     // 在图表中启用系列动画。
    m_ui->animatedComboBox->addItem("All Animations", QChart::AllAnimations);           // 在图表中启用所有动画类型。
}

// 填充图例框
void ThemeWidget::populateLegendBox()
{
    // 向图例组合框中添加项目
    m_ui->legendComboBox->addItem("No Legend ", 0);
    m_ui->legendComboBox->addItem("Legend Top", Qt::AlignTop);          //
    m_ui->legendComboBox->addItem("Legend Bottom", Qt::AlignBottom);
    m_ui->legendComboBox->addItem("Legend Left", Qt::AlignLeft);
    m_ui->legendComboBox->addItem("Legend Right", Qt::AlignRight);
}

// 创建图标
QChart *ThemeWidget::createAreaChart() const
{
    QChart *chart = new QChart();
    chart->setTitle("Area chart");  // 面积图 标题

    qDebug()<<" m_dataTable.count() = "<<m_dataTable.count()<<" m_dataTable[i].count() = "<<m_dataTable[0].count();
    // 较低的序列初始化为零值
    QLineSeries *lowerSeries = 0;   // 折线序列类以折线图的形式显示数据。
    QString name("Series ");
    int nameIndex = 0;
    for (int i(0); i < m_dataTable.count(); i++) {  // 3组
        QLineSeries *upperSeries = new QLineSeries(chart);  // 作用在图表内
        for (int j(0); j < m_dataTable[i].count(); j++) {   // 每组7个
            Data data = m_dataTable[i].at(j);               // 依次获取每一组数据
            if (lowerSeries) {  // >=第二次循环
                const QVector<QPointF>& points = lowerSeries->pointsVector();
                upperSeries->append(QPointF(j, points[i].y() + data.first.y()));
                // qDebug()<<" j = "<<j<<" lowerSeries->pointsVector() = "<<lowerSeries->pointsVector()<<" points[i].y() = "<<points[i].y();
            } else {
                upperSeries->append(QPointF(j, data.first.y()));    // data.first这对元素中的第一个元素。 7个数据
               // qDebug()<<" j = "<<j<<" upperSeries = "<<upperSeries->at(j);
            }
        }
        QAreaSeries *area = new QAreaSeries(upperSeries, lowerSeries);
        area->setName(name + QString::number(nameIndex));
        nameIndex++;
        chart->addSeries(area);         // 添加系列
        lowerSeries = upperSeries;      // lowerSeries第一次0 后面upperSeries上一个指向地址
    }

    chart->createDefaultAxes(); // 根据已添加到图表中的系列为图表创建轴。先前添加到图表中的任何轴都将被删除。
    chart->axes(Qt::Horizontal).first()->setRange(0, m_valueCount - 1); // x坐标范围
    chart->axes(Qt::Vertical).first()->setRange(0, m_valueMax);         // y坐标范围
    // 添加要添加标签的空间以添加标签和轴之间的空间
    QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first());
    Q_ASSERT(axisY); // 检查指针是否为空 会打印警告信息
    axisY->setLabelFormat("%.1f  ");

    return chart;
}

// 创建条形图
QChart *ThemeWidget::createBarChart(int valueCount) const
{
    Q_UNUSED(valueCount);
    QChart *chart = new QChart();
    chart->setTitle("Bar chart");

    QStackedBarSeries *series = new QStackedBarSeries(chart);
    for (int i(0); i < m_dataTable.count(); i++) {
        QBarSet *set = new QBarSet("Bar set " + QString::number(i));
        for (const Data &data : m_dataTable[i])
            *set << data.first.y();
        series->append(set);
    }
    chart->addSeries(series);

    chart->createDefaultAxes();
    chart->axes(Qt::Vertical).first()->setRange(0, m_valueMax * 2);
    // 添加要添加标签的空间以添加标签和轴之间的空间
    QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first());
    Q_ASSERT(axisY);
    axisY->setLabelFormat("%.1f  ");

    return chart;
}

QChart *ThemeWidget::createLineChart() const
{
    //![1]
    QChart *chart = new QChart();
    chart->setTitle("Line chart");
    //![1]

    //![2]
    QString name("Series ");
    int nameIndex = 0;
    for (const DataList &list : m_dataTable) { // 3组线
        QLineSeries *series = new QLineSeries(chart);
        for (const Data &data : list)
            series->append(data.first);
        series->setName(name + QString::number(nameIndex));
        nameIndex++;
        chart->addSeries(series);
    }
    //![2]

    //![3]
    chart->createDefaultAxes();
    chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax);
    chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount);
    //![3]
    //![4]
    // 添加要添加标签的空间以添加标签和轴之间的空间
    QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first());
    Q_ASSERT(axisY);
    axisY->setLabelFormat("%.1f  ");
    //![4]

    return chart;
}

// 创建饼图
QChart *ThemeWidget::createPieChart() const
{
    QChart *chart = new QChart();
    chart->setTitle("Pie chart");

    // qDebug()<<" [createPieChart]->m_dataTable[0] = "<<m_dataTable[0];
    QPieSeries *series = new QPieSeries(chart);
    for (const Data &data : m_dataTable[0]) {
        QPieSlice *slice = series->append(data.second, data.first.y());
        if (data == m_dataTable[0].first()) {       // 显示第0个
            slice->setLabelVisible();
            slice->setExploded();                   // 此属性用于保存切片是否与饼图分离。
            slice->setExplodeDistanceFactor(0.5);   // 确定切片离饼的距离。
        }
    }
    series->setPieSize(0.4);
    chart->addSeries(series);

    return chart;
}

// 创建样条曲线图
QChart *ThemeWidget::createSplineChart() const
{
    QChart *chart = new QChart();
    chart->setTitle("Spline chart");
    QString name("Series ");
    int nameIndex = 0;
    for (const DataList &list : m_dataTable) {
        QSplineSeries *series = new QSplineSeries(chart);
        for (const Data &data : list)
            series->append(data.first);
        series->setName(name + QString::number(nameIndex));
        nameIndex++;
        chart->addSeries(series);
    }

    chart->createDefaultAxes();
    chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax);
    chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount);

    // Add space to label to add space between labels and axis
    QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first());
    Q_ASSERT(axisY);
    axisY->setLabelFormat("%.1f  ");
    return chart;
}

// 创建散射图
QChart *ThemeWidget::createScatterChart() const
{
    // 散点图
    QChart *chart = new QChart();   // 不指定父类 因为他要作为别人的孩子
    chart->setTitle("Scatter chart");
    QString name("Series ");
    int nameIndex = 0;
    for (const DataList &list : m_dataTable) {
        QScatterSeries *series = new QScatterSeries(chart); // 创建散点图对象 并指定父类
        for (const Data &data : list)
            series->append(data.first);
        // 设置图例名称
        series->setName(name + QString::number(nameIndex));
        nameIndex++;
        chart->addSeries(series);   // 图表里面添加散点图
    }

    chart->createDefaultAxes();
    chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax);
    chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount);
    // 添加要添加标签的空间以添加标签和轴之间的空间
    QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first());
    Q_ASSERT(axisY);
    axisY->setLabelFormat("%.1f  ");
    return chart;
}

// 更新图表的样式 容易理解
void ThemeWidget::updateUI()
{
    // 获取主题枚举值
    QChart::ChartTheme theme = static_cast<QChart::ChartTheme>(
                m_ui->themeComboBox->itemData(m_ui->themeComboBox->currentIndex()).toInt());

    const auto charts = m_charts;   // 获取图标列表
    if (!m_charts.isEmpty() && m_charts.at(0)->chart()->theme() != theme) { // 不同时才设置
        for (QChartView *chartView : charts) {
            chartView->chart()->setTheme(theme);    // 设置图表主题
        }

        // 根据选定的主题设置调色板的颜色 为了让图表显示清晰，窗口主题色也要搭配
        QPalette pal = window()->palette(); // 当前QWidget窗口
        if (theme == QChart::ChartThemeLight) {
            pal.setColor(QPalette::Window, QRgb(0xf0f0f0));
            pal.setColor(QPalette::WindowText, QRgb(0x404044));
        } else if (theme == QChart::ChartThemeDark) {
            pal.setColor(QPalette::Window, QRgb(0x121218));
            pal.setColor(QPalette::WindowText, QRgb(0xd6d6d6));
        } else if (theme == QChart::ChartThemeBlueCerulean) {
            pal.setColor(QPalette::Window, QRgb(0x40434a));
            pal.setColor(QPalette::WindowText, QRgb(0xd6d6d6));
        } else if (theme == QChart::ChartThemeBrownSand) {
            pal.setColor(QPalette::Window, QRgb(0x9e8965));
            pal.setColor(QPalette::WindowText, QRgb(0x404044));
        } else if (theme == QChart::ChartThemeBlueNcs) {
            pal.setColor(QPalette::Window, QRgb(0x018bba));
            pal.setColor(QPalette::WindowText, QRgb(0x404044));
        } else if (theme == QChart::ChartThemeHighContrast) {
            pal.setColor(QPalette::Window, QRgb(0xffab03));
            pal.setColor(QPalette::WindowText, QRgb(0x181818));
        } else if (theme == QChart::ChartThemeBlueIcy) {
            pal.setColor(QPalette::Window, QRgb(0xcee7f0));
            pal.setColor(QPalette::WindowText, QRgb(0x404044));
        } else {
            pal.setColor(QPalette::Window, QRgb(0xf0f0f0));
            pal.setColor(QPalette::WindowText, QRgb(0x404044));
        }
        window()->setPalette(pal);
    }

    // 更新反锯齿 图表变得光滑
    bool checked = m_ui->antialiasCheckBox->isChecked();
    for (QChartView *chart : charts) // 针对所有图表
        chart->setRenderHint(QPainter::Antialiasing, checked);

    // 更新动画选项
    QChart::AnimationOptions options(
                m_ui->animatedComboBox->itemData(m_ui->animatedComboBox->currentIndex()).toInt());
    if (!m_charts.isEmpty() && m_charts.at(0)->chart()->animationOptions() != options) {
        for (QChartView *chartView : charts)
            chartView->chart()->setAnimationOptions(options);
    }

    // 更新图例对齐
    Qt::Alignment alignment(
                m_ui->legendComboBox->itemData(m_ui->legendComboBox->currentIndex()).toInt());

    if (!alignment) {
        for (QChartView *chartView : charts)
            chartView->chart()->legend()->hide(); // 图例隐藏 默认是不显示图例的
    } else {
        for (QChartView *chartView : charts) {
            chartView->chart()->legend()->setAlignment(alignment);
            chartView->chart()->legend()->show();
        }
    }
}

